using System;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Threading;

namespace ICommonLibrary.Collection {

    public class MRUCache<TK, TV> : IDeserializationCallback {

        private const int DefaultStrongRefCount = 128;

        private object _lock;

        private readonly int _strongReferenceCount;

        [NonSerialized]
        private LRUMap _cache;

        public MRUCache() : this(DefaultStrongRefCount) { }

        public MRUCache(int strongReferenceCount) {
            _strongReferenceCount = strongReferenceCount;
            _cache = new LRUMap(strongReferenceCount);
        }

        private object Lock {
            get {
                if (_lock == null)
                    Interlocked.CompareExchange(ref _lock, new object(), null);

                return _lock;
            }
        }

        void IDeserializationCallback.OnDeserialization(object sender) {
            _cache = new LRUMap(_strongReferenceCount);
        }

        public TV this[TK key] {
            [MethodImpl(MethodImplOptions.Synchronized)]
            get {
                lock (Lock) {
                    object o = _cache[key];
                    if (o != null) {
                        return (TV)o;
                    }
                    return default(TV);
                }
            }
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void Put(TK key, TV value) {
            lock (Lock) {
                _cache.Add(key, value);
            }
        }

        public int Count {
            [MethodImpl(MethodImplOptions.Synchronized)]
            get {
                lock (Lock) {
                    return _cache.Count;
                }
            }
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void Clear() {
            lock (Lock) {
                _cache.Clear();
            }
        }
    }
}